System interaction
Environment
R 中词法作用域的机理通过环境实现。
函数优先在内部环境搜索用到的变量,搜索不到时,就到上一级环境搜索。
每加载一个扩展包,这个包的环境都会插入搜索路径,并位于全局环境之前。
ls()/objects() |
列出当前工作空间的所有对象 |
ls.str(mode=, pattern=) |
显示当前环境的结构。mode 参数过滤对象类型,pattern 参数为正则表达式,过滤对象名 |
rm() |
删除一个或更多个对象。 |
rm(list=ls(all=TRUE)) |
删除所有对象 |
getOption() |
查看全局选项 |
options() |
设定全局选项。如 digits=7, 设定显示数字的位数。warn=0, 可以改为1,在警告产生时立即显示。 |
Path
dir.create() |
创建一个目录 |
getwd()/setwd() |
查看/设定当前工作目录 |
Memory
tracemem()查看对象的内存地址
Time
Sys.sleep() 使程序暂停若干秒,一般用于某些循环(爬虫、动画等),故意降低运行频率
sys.time() 读取系统时间
t1 <- Sys.time()
...
t2 <- Sys.time()
print(t2 - t1) # 中间代码的运行时间
Help
help(functionName)或?functionName |
查看一个函数的用法 |
example(functionName) |
运行帮主文件中的例子 |
help.search(keyword)或??keyword |
在==所有包==的文档中搜索关键词 |
help(package = "") |
查看一个包的帮助 |
help(options) |
显示可用选项的说明 |
options() |
显示或设置当前选项 |
History
history(#) |
显示最近使用过的#个命令(默认值为25) |
savehistory("myfile") |
保存命令历史到文件myfile中(默认值为.Rhistory) |
loadhistory("myfile") |
载入一个命令历史文件(默认值为.Rhistory) |
Modularization of R Coding
R Package
install.packages("package") |
安装包 |
installed.packages() |
返回一个数据框,内含所有已安装的包的多项信息 |
library() |
无参数时,显示库中已经安装了哪些包 |
library(package) |
有参数时,载入包 |
unloadNamespace(package) |
解除载入一个包 |
search() |
查看环境中已经加载了哪些包 |
getOption('defaultPackages') |
查看默认加载的 R 包 |
update.packages("package") |
升级包。若不填入参数,则自动升级所有可以升级的包 |
package::function() |
调用相应包的函数(有时多个包用同一个名字命名不同的函数,会发生冲突,只能这样引用) |
data() |
查看所有预先提供的数据 |
data(package="") |
查看某个包所有预先提供的数据 |
data(dataset_name, package=) |
读入包中数据 |
# 查看 R 包的下载次数
library(cranlogs)
fashion <- function(package_name) {
d <- cran_downloads(
package = package_name,
from = "2020-01-01",
to = "2021-05-31"
)
sum(d$count)
}
fashion('ggplot2')
fashion("tidyverse")
fashion("data.table")
fashion("plotly")
R Script
最佳实践
由于 R 的懒加载特性,模块中的代码不会运行,故 .R 脚本文件作为模块时,不必加载配置常量和包,纯写函数即可,所有的包和配置由主文件加载。
Code Style
Google R Style
一般性规则
- 尽量避免使用
attach(), detach()
- Error 应该使用
Stop()来抛出
- S3 类和 S4 类的函数不要一起使用
命名
对象命名以句号.分隔,不用下划线
函数名首字母大写,驼峰式命名法,不用句号分隔
注释
注释行以#开头,后加一个空格
代码行内短注释需要在代码后面空两格,然后#,再加一个空格
对变量和函数的说明写在它们的上方(紧邻),VSCode 的 R 插件能够自动识别
总体布局与顺序
- 版权声明
- 作者信息
- 文件说明, 包括程序的目的,输入以及输出
- source() 和 library() 说明
- 函数定义
- 可执行语句, 如果有的话 (例如, print, plot)
单元测试应在另一个独立的的文件中进行
IDE
Rstudio
R Studio cheatsheet 预览:
| Ctrl + Shift + A |
选中部分行后,格式化代码 |
| Ctrl + Alt + I |
Insert chunk |
| Alt + - |
插入 <- |
| Ctrl + Shift + M |
插入%>% |
| Alt + Shift + K |
显示快捷键 |
| Ctrl + Shift + N |
新建脚本 .r文件 |
Ctrl + Enter Ctrl + Shift + Enter Ctrl + Alt + R |
运行一行代码 运行代码块 运行全部代码 |
| Shift + Home/End |
选中光标到行首/末之间的部分 |
| Tab / Ctrl+Space |
自动补齐 输入完函数名,按tab,自动添加开括号(和闭括号)。 |
| Ctrl + Shift + C |
注释/取消注释 |
| F1 |
查看帮助 |
| Ctrl+ ↑ |
在 Console 中输入“xxx”,然后按 Ctrl+ ↑。就可以列出所有输入过的以“xxx”开头的命令。 |
VSCode
优点
- 鼠标悬停,即可显示变量的定义信息和函数的帮助文档(仅限 R 包中函数的官方文档和本文件中自定义函数的定义,无法显示引入模块中的自定义函数的定义),省去了查阅文档的大量时间
- 保存(Ctrl+S)时自动格式化
配置步骤
安装 R 包 languageserver
install.packages("languageserver")
在 VSCode 扩展商店中安装 R 插件。
- 安装完成后在 VSCode 设置中搜索
r.rterm.option,删除--no-save,--no-restore,添加--no-site-file和 R.exe 的路径--r-binary=C:\Program Files\R\R-4.1.3\bin\R.exe
安装 Radian:一款现代的 R console,它是用 Python 编写的
安装完成后在 cmd 中输入 radian 查看是否安装成功。
若出现 “cannot determine R HOME”,可能存在多个R路径(如新安装了4.1.2版本),而 Radian 无法识别。解决这个问题的方法仍然是在 VSCode 中设置 R.exe 的路径。除了搜索r.rterm.option进行修改,也可以在设置界面的右上角打开 setting.json文件直接修改:
"r.rterm.option": [
"--no-site-file",
"--r-binary=C:\\Program Files\\R\\R-4.1.3\\bin\\R.exe"
],
VSCode 中 Radian 相关设置
- 搜索
r.rterm.windows,将其设置为 radian.exe 的路径。在 cmd 中(powershell 不行)输入 where radian 可以获取其路径。
- 搜索
R: Bracketed Paste并勾选,否则 Radian 不会启用
- 搜索
r.sessionWatcher并勾选
如何在 VSCODE 中高效使用 R 语言 (图文详解)_Baimoc-CSDN博客
LS0tDQp0aXRsZTogIlIgRW5naW5lZXJpbmciDQpzdWJ0aXRsZTogJ1N5c3RlbSwgTW9kdWxhcml6YXRpb24sIENvZGUgU3R5bGUgYW5kIElERScNCmF1dGhvcjogIkh1bW9vbiINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogaHRtbF9kb2N1bWVudA0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0NCnNvdXJjZSgiLi4vUm1hcmtkb3duLXRlbXBsYXRlL1JtYXJrZG93bl9jb25maWcuUiIpDQoNCiMjIGdsb2JhbCBvcHRpb25zID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIHdpZHRoID0gY29uZmlnJHdpZHRoLA0KICBmaWcud2lkdGggPSBjb25maWckZmlnLndpZHRoLA0KICBmaWcuYXNwID0gY29uZmlnJGZpZy5hc3AsDQogIG91dC53aWR0aCA9IGNvbmZpZyRvdXQud2lkdGgsDQogIGZpZy5hbGlnbiA9IGNvbmZpZyRmaWcuYWxpZ24sDQogIGZpZy5wYXRoID0gY29uZmlnJGZpZy5wYXRoLA0KICBmaWcuc2hvdyA9IGNvbmZpZyRmaWcuc2hvdywNCiAgd2FybiA9IGNvbmZpZyR3YXJuLA0KICB3YXJuaW5nID0gY29uZmlnJHdhcm5pbmcsDQogIG1lc3NhZ2UgPSBjb25maWckbWVzc2FnZSwNCiAgZWNobyA9IGNvbmZpZyRlY2hvLA0KICBldmFsID0gY29uZmlnJGV2YWwsDQogIHRpZHkgPSBjb25maWckdGlkeSwNCiAgY29tbWVudCA9IGNvbmZpZyRjb21tZW50LA0KICBjb2xsYXBzZSA9IGNvbmZpZyRjb2xsYXBzZSwNCiAgY2FjaGUgPSBjb25maWckY2FjaGUsDQogIGNhY2hlLmNvbW1lbnRzID0gY29uZmlnJGNhY2hlLmNvbW1lbnRzLA0KICBhdXRvZGVwID0gY29uZmlnJGF1dG9kZXANCikNCmBgYA0KDQojIyBTeXN0ZW0gaW50ZXJhY3Rpb24NCg0KIyMjIEVudmlyb25tZW50DQoNClIg5Lit6K+N5rOV5L2c55So5Z+f55qE5py655CG6YCa6L+H546v5aKD5a6e546w44CCDQoNCuWHveaVsOS8mOWFiOWcqOWGhemDqOeOr+Wig+aQnOe0oueUqOWIsOeahOWPmOmHj++8jOaQnOe0ouS4jeWIsOaXtu+8jOWwseWIsOS4iuS4gOe6p+eOr+Wig+aQnOe0ouOAgg0KDQrmr4/liqDovb3kuIDkuKrmianlsZXljIXvvIzov5nkuKrljIXnmoTnjq/looPpg73kvJrmj5LlhaXmkJzntKLot6/lvoTvvIzlubbkvY3kuo7lhajlsYDnjq/looPkuYvliY3jgIINCg0KfCDnjq/looPkuqTkupLlh73mlbAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8IGBscygpYC9gb2JqZWN0cygpYCAgICAgICAgfCDliJflh7rlvZPliY3lt6XkvZznqbrpl7TnmoTmiYDmnInlr7nosaEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGxzLnN0cihtb2RlPSwgcGF0dGVybj0pYCB8IOaYvuekuuW9k+WJjeeOr+Wig+eahOe7k+aehOOAgm1vZGUg5Y+C5pWw6L+H5ruk5a+56LGh57G75Z6L77yMcGF0dGVybiDlj4LmlbDkuLrmraPliJnooajovr7lvI/vvIzov4fmu6Tlr7nosaHlkI0gfA0KfCBgcm0oKWAgICAgICAgICAgICAgICAgICAgIHwg5Yig6Zmk5LiA5Liq5oiW5pu05aSa5Liq5a+56LGh44CCICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgcm0obGlzdD1scyhhbGw9VFJVRSkpYCAgIHwg5Yig6Zmk5omA5pyJ5a+56LGhICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGdldE9wdGlvbigpYCAgICAgICAgICAgICB8IOafpeeci+WFqOWxgOmAiemhuSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBvcHRpb25zKClgICAgICAgICAgICAgICAgfCDorr7lrprlhajlsYDpgInpobnjgILlpoIgZGlnaXRzPTcsIOiuvuWumuaYvuekuuaVsOWtl+eahOS9jeaVsOOAgndhcm49MCwg5Y+v5Lul5pS55Li6Me+8jOWcqOitpuWRiuS6p+eUn+aXtueri+WNs+aYvuekuuOAgiB8DQoNCiMjIyBQYXRoDQoNCnwgV29ya2luZyAgRGlyZWN0b3J5ICB8ICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC0tLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0gfA0KfCBgZGlyLmNyZWF0ZSgpYCAgICAgIHwg5Yib5bu65LiA5Liq55uu5b2VICAgICAgICAgIHwNCnwgYGdldHdkKClgL2BzZXR3ZCgpYCB8IOafpeeciy/orr7lrprlvZPliY3lt6XkvZznm67lvZUgfA0KDQoNCg0KIyMjIE1lbW9yeQ0KDQpgdHJhY2VtZW0oKWDmn6XnnIvlr7nosaHnmoTlhoXlrZjlnLDlnYANCg0KIyMjIFRpbWUNCg0KYFN5cy5zbGVlcCgpYCDkvb/nqIvluo/mmoLlgZzoi6XlubLnp5LvvIzkuIDoiKznlKjkuo7mn5Dkupvlvqrnjq/vvIjniKzomavjgIHliqjnlLvnrYnvvInvvIzmlYXmhI/pmY3kvY7ov5DooYzpopHnjocNCg0KYHN5cy50aW1lKClgIOivu+WPluezu+e7n+aXtumXtA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnQxIDwtIFN5cy50aW1lKCkNCi4uLg0KdDIgPC0gU3lzLnRpbWUoKQ0KcHJpbnQodDIgLSB0MSkgIyDkuK3pl7Tku6PnoIHnmoTov5DooYzml7bpl7QNCmBgYA0KDQoNCiMjIyBIZWxwDQoNCnwgSGVscCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gfA0KfCBgaGVscChmdW5jdGlvbk5hbWUpYOaIlmA/ZnVuY3Rpb25OYW1lYCB8IOafpeeci+S4gOS4quWHveaVsOeahOeUqOazlSAgICAgICAgICAgICB8DQp8IGBleGFtcGxlKGZ1bmN0aW9uTmFtZSlgICAgICAgICAgICAgICAgfCDov5DooYzluK7kuLvmlofku7bkuK3nmoTkvovlrZAgICAgICAgICAgIHwNCnwgYGhlbHAuc2VhcmNoKGtleXdvcmQpYOaIlmA/P2tleXdvcmRgICAgfCDlnKg9PeaJgOacieWMhT0955qE5paH5qGj5Lit5pCc57Si5YWz6ZSu6K+NIHwNCnwgYGhlbHAocGFja2FnZSA9ICIiKWAgICAgICAgICAgICAgICAgICB8IOafpeeci+S4gOS4quWMheeahOW4ruWKqSAgICAgICAgICAgICAgIHwNCnwgYGhlbHAob3B0aW9ucylgICAgICAgICAgICAgICAgICAgICAgICB8IOaYvuekuuWPr+eUqOmAiemhueeahOivtOaYjiAgICAgICAgICAgICB8DQp8IGBvcHRpb25zKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmmL7npLrmiJborr7nva7lvZPliY3pgInpobkgICAgICAgICAgICAgfA0KDQojIyMgSGlzdG9yeQ0KDQp8IEhpc3RvcnkgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwNCnwgYGhpc3RvcnkoIylgICAgICAgICAgICAgfCDmmL7npLrmnIDov5Hkvb/nlKjov4fnmoQj5Liq5ZG95Luk77yI6buY6K6k5YC85Li6MjXvvIkgICAgICAgICAgIHwNCnwgYHNhdmVoaXN0b3J5KCJteWZpbGUiKWAgfCDkv53lrZjlkb3ku6Tljoblj7LliLDmlofku7ZteWZpbGXkuK3vvIjpu5jorqTlgLzkuLouUmhpc3RvcnnvvIkgfA0KfCBgbG9hZGhpc3RvcnkoIm15ZmlsZSIpYCB8IOi9veWFpeS4gOS4quWRveS7pOWOhuWPsuaWh+S7tu+8iOm7mOiupOWAvOS4ui5SaGlzdG9yee+8iSAgICAgICB8DQoNCg0KDQojIyBNb2R1bGFyaXphdGlvbiBvZiBSIENvZGluZw0KDQojIyMgUiBQYWNrYWdlDQoNCnwgUGFja2FnZSAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIHwNCnwgYGluc3RhbGwucGFja2FnZXMoInBhY2thZ2UiKWAgIHwg5a6J6KOF5YyFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGluc3RhbGxlZC5wYWNrYWdlcygpYCAgICAgICAgIHwg6L+U5Zue5LiA5Liq5pWw5o2u5qGG77yM5YaF5ZCr5omA5pyJ5bey5a6J6KOF55qE5YyF55qE5aSa6aG55L+h5oGvICAgICAgICAgICAgICAgICB8DQp8IGBsaWJyYXJ5KClgICAgICAgICAgICAgICAgICAgICB8IOaXoOWPguaVsOaXtu+8jOaYvuekuuW6k+S4reW3sue7j+WuieijheS6huWTquS6m+WMhSAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGxpYnJhcnkocGFja2FnZSlgICAgICAgICAgICAgIHwg5pyJ5Y+C5pWw5pe277yM6L295YWl5YyFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgdW5sb2FkTmFtZXNwYWNlKHBhY2thZ2UpYCAgICAgfCDop6PpmaTovb3lhaXkuIDkuKrljIUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHNlYXJjaCgpYCAgICAgICAgICAgICAgICAgICAgIHwg5p+l55yL546v5aKD5Lit5bey57uP5Yqg6L295LqG5ZOq5Lqb5YyFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBnZXRPcHRpb24oJ2RlZmF1bHRQYWNrYWdlcycpYCB8IOafpeeci+m7mOiupOWKoOi9veeahCBSIOWMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHVwZGF0ZS5wYWNrYWdlcygicGFja2FnZSIpYCAgIHwg5Y2H57qn5YyF44CC6Iul5LiN5aGr5YWl5Y+C5pWw77yM5YiZ6Ieq5Yqo5Y2H57qn5omA5pyJ5Y+v5Lul5Y2H57qn55qE5YyFICAgICAgICAgICAgIHwNCnwgYHBhY2thZ2U6OmZ1bmN0aW9uKClgICAgICAgICAgIHwg6LCD55So55u45bqU5YyF55qE5Ye95pWw77yI5pyJ5pe2KirlpJrkuKrljIXnlKjlkIzkuIDkuKrlkI3lrZflkb3lkI3kuI3lkIznmoTlh73mlbDvvIzkvJrlj5HnlJ/lhrLnqoHvvIzlj6rog73ov5nmoLflvJXnlKgqKu+8iSB8DQp8IGBkYXRhKClgICAgICAgICAgICAgICAgICAgICAgICB8IOafpeeci+aJgOaciemihOWFiOaPkOS+m+eahOaVsOaNriAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGRhdGEocGFja2FnZT0iIilgICAgICAgICAgICAgIHwg5p+l55yL5p+Q5Liq5YyF5omA5pyJ6aKE5YWI5o+Q5L6b55qE5pWw5o2uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgZGF0YShkYXRhc2V0X25hbWUsIHBhY2thZ2U9KWAgfCDor7vlhaXljIXkuK3mlbDmja4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KDQpgYGBSDQojIOafpeeciyBSIOWMheeahOS4i+i9veasoeaVsA0KbGlicmFyeShjcmFubG9ncykNCg0KZmFzaGlvbiA8LSBmdW5jdGlvbihwYWNrYWdlX25hbWUpIHsNCiAgZCA8LSBjcmFuX2Rvd25sb2FkcygNCiAgICBwYWNrYWdlID0gcGFja2FnZV9uYW1lLA0KICAgIGZyb20gPSAiMjAyMC0wMS0wMSIsDQogICAgdG8gPSAiMjAyMS0wNS0zMSINCiAgKQ0KICBzdW0oZCRjb3VudCkNCn0NCg0KZmFzaGlvbignZ2dwbG90MicpDQpmYXNoaW9uKCJ0aWR5dmVyc2UiKQ0KZmFzaGlvbigiZGF0YS50YWJsZSIpDQpmYXNoaW9uKCJwbG90bHkiKQ0KYGBgDQoNCiMjIyBSIFNjcmlwdA0KDQojIyMjIOivreazlQ0KDQpgc291cmNlKCd4eHguUicpYCANCg0KIyMjIyDmnIDkvbPlrp7ot7UNCg0K55Sx5LqOIFIg55qE5oeS5Yqg6L2954m55oCn77yM5qih5Z2X5Lit55qE5Luj56CB5LiN5Lya6L+Q6KGM77yM5pWFIC5SIOiEmuacrOaWh+S7tuS9nOS4uuaooeWdl+aXtu+8jOS4jeW/heWKoOi9vemFjee9ruW4uOmHj+WSjOWMhe+8jOe6r+WGmeWHveaVsOWNs+WPr++8jOaJgOacieeahOWMheWSjOmFjee9rueUseS4u+aWh+S7tuWKoOi9veOAgg0KDQoNCg0KIyMgQ29kZSBTdHlsZQ0KDQojIyMg5qC85byP5YyWDQoNCiMjIyMgc3R5bGVyIOWMhQ0KDQpbc3R5bGVyIC0gQSBub24taW52YXNpdmUgc291cmNlIGNvZGUgZm9ybWF0dGVyIGZvciBSIChsb3Jlbnp3YWx0aGVydC5naXRodWIuaW8pXShodHRwczovL2xvcmVuendhbHRoZXJ0LmdpdGh1Yi5pby9zdHlsZXJwb3N0LykNCg0KYGBgUg0KaW5zdGFsbC5wYWNrYWdlcygic3R5bGVyIikNCmBgYA0KDQotIGBzdHlsZV90ZXh0KClgIHN0eWxlcyBhIHN0cmluZw0KLSBgc3R5bGVfZmlsZSgpYCBzdHlsZXMgUiBhbmQgUm1kIGZpbGVzDQotIGBzdHlsZV9kaXIoKWAgc3R5bGVzIGFsbCBSIGFuZC9vciBSbWQgZmlsZXMgaW4gYSBkaXJlY3RvcnkuDQotICoq5pyA5bi455SoKioNCiAgLSBSU3R1ZGlvIGBBZGRpbnNgIOiPnOWNle+8jHN0eWxlcyB0aGUgYWN0aXZlIGZpbGUgUiBvciBSbWQgZmlsZSwgb3IgdGhlIGhpZ2hsaWdodGVkIGNvZGUuDQogIC0g5oiW5ZyoIGNvbnNvbGUg5Lit6L6T5YWlYHN0eWxlcjo6OnN0eWxlX2FjdGl2ZV9maWxlKClgDQogIA0KDQojIyMgR29vZ2xlIFIgU3R5bGUNCg0KIyMjIyDkuIDoiKzmgKfop4TliJkNCg0KLSDlsL3ph4/pgb/lhY3kvb/nlKhgYXR0YWNoKClgLCBgZGV0YWNoKClgDQotIEVycm9yIOW6lOivpeS9v+eUqGBTdG9wKClg5p2l5oqb5Ye6DQotIFMzIOexu+WSjCBTNCDnsbvnmoTlh73mlbDkuI3opoHkuIDotbfkvb/nlKgNCg0KIyMjIyDlkb3lkI0NCg0K5a+56LGh5ZG95ZCN5Lul5Y+l5Y+3YC5g5YiG6ZqU77yM5LiN55So5LiL5YiS57q/DQoNCuWHveaVsOWQjemmluWtl+avjeWkp+WGme+8jOmpvOWzsOW8j+WRveWQjeazle+8jOS4jeeUqOWPpeWPt+WIhumalA0KDQojIyMjIOazqOmHig0KDQrms6jph4rooYzku6VgI2DlvIDlpLTvvIzlkI7liqDkuIDkuKrnqbrmoLwNCg0K5Luj56CB6KGM5YaF55+t5rOo6YeK6ZyA6KaB5Zyo5Luj56CB5ZCO6Z2i56m65Lik5qC877yM54S25ZCOYCNg77yM5YaN5Yqg5LiA5Liq56m65qC8DQoNCioq5a+55Y+Y6YeP5ZKM5Ye95pWw55qE6K+05piO5YaZ5Zyo5a6D5Lus55qE5LiK5pa577yI57Sn6YK777yJ77yMVlNDb2RlIOeahCBSIOaPkuS7tuiDveWkn+iHquWKqOivhuWIqyoqDQoNCiMjIyMg5oC75L2T5biD5bGA5LiO6aG65bqPDQoNCjEuIOeJiOadg+WjsOaYjg0KMi4g5L2c6ICF5L+h5oGvDQozLiDmlofku7bor7TmmI4sIOWMheaLrOeoi+W6j+eahOebrueahO+8jOi+k+WFpeS7peWPiui+k+WHug0KNC4gc291cmNlKCkg5ZKMIGxpYnJhcnkoKSDor7TmmI4NCjUuIOWHveaVsOWumuS5iQ0KNi4g5Y+v5omn6KGM6K+t5Y+lLCDlpoLmnpzmnInnmoTor50gKOS+i+WmgiwgcHJpbnQsIHBsb3QpDQoNCuWNleWFg+a1i+ivleW6lOWcqOWPpuS4gOS4queLrOeri+eahOeahOaWh+S7tuS4rei/m+ihjA0KDQoNCg0KIyMgSURFDQoNCiMjIyBSc3R1ZGlvDQoNCltSIFN0dWRpbyBjaGVhdHNoZWV0XShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcnN0dWRpby9jaGVhdHNoZWV0cy9tYWluL3JzdHVkaW8taWRlLnBkZikg6aKE6KeI77yaDQoNCjxvYmplY3QgZGF0YT0iLi4vcGRmL2NoZWF0c2hlZXQtcnN0dWRpby1pZGUucGRmIiB0eXBlPSJhcHBsaWNhdGlvbi9wZGYiIHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiPjwvb2JqZWN0Pg0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmRvd25sb2FkdGhpczo6ZG93bmxvYWRfZmlsZSgNCiAgcGF0aCA9ICIuLi9wZGYvY2hlYXRzaGVldC1yc3R1ZGlvLWlkZS5wZGYiLA0KICBvdXRwdXRfbmFtZSA9ICJjaGVhdHNoZWV0LXJzdHVkaW8taWRlIiwNCiAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkIGNoZWF0c2hlZXQiLA0KICBidXR0b25fdHlwZSA9ICJzdWNjZXNzIiwNCiAgc2VsZl9jb250YWluZWQgPSBGQUxTRQ0KKQ0KYGBgDQoNCg0KfCBTaG9ydGN1dHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSB8DQp8IEN0cmwgKyBTaGlmdCArIEEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDpgInkuK3pg6jliIbooYzlkI7vvIzmoLzlvI/ljJbku6PnoIEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBDdHJsICsgQWx0ICsgSSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgSW5zZXJ0IGNodW5rICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQWx0ICsgLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaPkuWFpSA8LSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCArIFNoaWZ0ICsgTSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaPkuWFpSU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQWx0ICsgU2hpZnQgKyBLICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaYvuekuuW/q+aNt+mUriAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCArIFNoaWZ0ICsgTiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaWsOW7uuiEmuacrCAucuaWh+S7tiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEN0cmwgKyBFbnRlcjxiciAvPkN0cmwgKyBTaGlmdCArIEVudGVyPGJyIC8+Q3RybCArIEFsdCArIFIgfCDov5DooYzkuIDooYzku6PnoIE8YnIgLz7ov5DooYzku6PnoIHlnZc8YnIgLz7ov5DooYzlhajpg6jku6PnoIEgICAgICAgICAgICAgICB8DQp8IFNoaWZ0ICsgSG9tZS9FbmQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDpgInkuK3lhYnmoIfliLDooYzpppYv5pyr5LmL6Ze055qE6YOo5YiGICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgVGFiIC8gQ3RybCtTcGFjZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOiHquWKqOihpem9kDxiciAvPui+k+WFpeWujOWHveaVsOWQje+8jOaMiXRhYu+8jOiHquWKqOa3u+WKoOW8gOaLrOWPtyjlkozpl63mi6zlj7cp44CCIHwNCnwgQ3RybCArIFNoaWZ0ICsgQyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOazqOmHii/lj5bmtojms6jph4ogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEYxICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmn6XnnIvluK7liqkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgQ3RybCsg4oaRICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5ZyoIENvbnNvbGUg5Lit6L6T5YWl4oCceHh44oCd77yM54S25ZCO5oyJIEN0cmwrIOKGkeOAguWwseWPr+S7peWIl+WHuuaJgOaciei+k+WFpei/h+eahOS7peKAnHh4eOKAneW8gOWktOeahOWRveS7pOOAgiB8DQoNCiMjIyBWU0NvZGUNCg0KIyMjIyDkvJjngrkNCg0KLSDpvKDmoIfmgqzlgZzvvIzljbPlj6/mmL7npLrlj5jph4/nmoTlrprkuYnkv6Hmga/lkozlh73mlbDnmoTluK7liqnmlofmoaPvvIjku4XpmZAgUiDljIXkuK3lh73mlbDnmoTlrpjmlrnmlofmoaPlkozmnKzmlofku7bkuK3oh6rlrprkuYnlh73mlbDnmoTlrprkuYnvvIzml6Dms5XmmL7npLrlvJXlhaXmqKHlnZfkuK3nmoToh6rlrprkuYnlh73mlbDnmoTlrprkuYnvvInvvIznnIHljrvkuobmn6XpmIXmlofmoaPnmoTlpKfph4/ml7bpl7QNCi0g5L+d5a2Y77yIQ3RybCtT77yJ5pe26Ieq5Yqo5qC85byP5YyWDQoNCiMjIyMg6YWN572u5q2l6aqkDQoNCjEuIOWuieijhSBSIOWMhSBsYW5ndWFnZXNlcnZlcg0KDQogICBgYGBSDQogICBpbnN0YWxsLnBhY2thZ2VzKCJsYW5ndWFnZXNlcnZlciIpDQogICBgYGANCg0KMi4g5ZyoIFZTQ29kZSDmianlsZXllYblupfkuK3lronoo4UgUiDmj5Lku7bjgIINCg0KICAgMS4g5a6J6KOF5a6M5oiQ5ZCO5ZyoIFZTQ29kZSDorr7nva7kuK3mkJzntKJgci5ydGVybS5vcHRpb25g77yM5Yig6ZmkYC0tbm8tc2F2ZSwtLW5vLXJlc3RvcmVg77yM5re75YqgYC0tbm8tc2l0ZS1maWxlYOWSjCBSLmV4ZSDnmoTot6/lvoRgLS1yLWJpbmFyeT1DOlxQcm9ncmFtIEZpbGVzXFJcUi00LjEuM1xiaW5cUi5leGVgDQoNCjMuIOWuieijhSBSYWRpYW7vvJrkuIDmrL7njrDku6PnmoQgUiBjb25zb2xl77yM5a6D5piv55SoIFB5dGhvbiDnvJblhpnnmoQNCg0KICAgYGBgcG93ZXJzaGVsbA0KICAgcGlwIGluc3RhbGwgcmFkaWFuDQogICBgYGANCg0KICAgMS4g5a6J6KOF5a6M5oiQ5ZCO5ZyoIGNtZCDkuK3ovpPlhaUgYHJhZGlhbmAg5p+l55yL5piv5ZCm5a6J6KOF5oiQ5Yqf44CCDQoNCiAgIDIuIOiLpeWHuueOsCDigJxjYW5ub3QgZGV0ZXJtaW5lIFIgSE9NReKAne+8jOWPr+iDveWtmOWcqOWkmuS4qlLot6/lvoTvvIjlpoLmlrDlronoo4XkuoY0LjEuMueJiOacrO+8ie+8jOiAjCBSYWRpYW4g5peg5rOV6K+G5Yir44CC6Kej5Yaz6L+Z5Liq6Zeu6aKY55qE5pa55rOV5LuN54S25piv5ZyoIFZTQ29kZSDkuK3orr7nva4gUi5leGUg55qE6Lev5b6E44CC6Zmk5LqG5pCc57SiYHIucnRlcm0ub3B0aW9uYOi/m+ihjOS/ruaUue+8jOS5n+WPr+S7peWcqOiuvue9rueVjOmdoueahOWPs+S4iuinkuaJk+W8gCBzZXR0aW5nLmpzb27mlofku7bnm7TmjqXkv67mlLnvvJoNCg0KICAgICAgYGBgYGpzb24NCiAgICAgICAgInIucnRlcm0ub3B0aW9uIjogWw0KICAgICAgICAgICItLW5vLXNpdGUtZmlsZSIsDQogICAgICAgICAgIi0tci1iaW5hcnk9QzpcXFByb2dyYW0gRmlsZXNcXFJcXFItNC4xLjNcXGJpblxcUi5leGUiDQogICAgICAgIF0sDQogICAgICBgYGBgDQoNCjQuIFZTQ29kZSDkuK0gUmFkaWFuIOebuOWFs+iuvue9rg0KDQogICAxLiDmkJzntKIgYHIucnRlcm0ud2luZG93c2DvvIzlsIblhbborr7nva7kuLogcmFkaWFuLmV4ZSDnmoTot6/lvoTjgILlnKggY21kIOS4re+8iHBvd2Vyc2hlbGwg5LiN6KGM77yJ6L6T5YWlIGB3aGVyZSByYWRpYW5gIOWPr+S7peiOt+WPluWFtui3r+W+hOOAgg0KICAgMi4g5pCc57SiYFI6IEJyYWNrZXRlZCBQYXN0ZWDlubbli77pgInvvIzlkKbliJkgUmFkaWFuIOS4jeS8muWQr+eUqA0KICAgMy4g5pCc57SiYHIuc2Vzc2lvbldhdGNoZXJg5bm25Yu+6YCJDQoNCg0KDQpb5aaC5L2V5ZyoIFZTQ09ERSDkuK3pq5jmlYjkvb/nlKggUiDor63oqIAg77yI5Zu+5paH6K+m6Kej77yJX0JhaW1vYy1DU0RO5Y2a5a6iXShodHRwczovL2Jsb2cuY3Nkbi5uZXQvdTAxMTI2MjI1My9hcnRpY2xlL2RldGFpbHMvMTEzODM3NzIwKQ0K